local function Get_Name_And_Inspect_Table()
    return TUNING.NPC_CHAT_TABLE[ type(TUNING.__NPC_LANGUAGE)=="function" and TUNING.__NPC_LANGUAGE() or TUNING.__NPC_LANGUAGE ] ["npc_names_and_inspect"] or {}
end

local function fn()
    local inst = CreateEntity()
    
    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddSoundEmitter()
    inst.entity:AddDynamicShadow()
    inst.entity:AddMiniMapEntity()
    inst.entity:AddLight()
    inst.entity:AddLightWatcher()
    inst.entity:AddNetwork()

    inst.Transform:SetFourFaced()

    inst.AnimState:SetBank("wilson")
    inst.AnimState:SetBuild("wilson")

    inst.AnimState:PlayAnimation("idle")

    inst.AnimState:Hide("ARM_carry")
    inst.AnimState:Hide("HAT")
    inst.AnimState:Hide("HAIR_HAT")
    inst.AnimState:Show("HAIR_NOHAT")
    inst.AnimState:Show("HAIR")
    inst.AnimState:Show("HEAD")
    inst.AnimState:Hide("HEAD_HAT")

    inst.AnimState:OverrideSymbol("fx_wipe", "wilson_fx", "fx_wipe")
    inst.AnimState:OverrideSymbol("fx_liquid", "wilson_fx", "fx_liquid")
    inst.AnimState:OverrideSymbol("shadow_hands", "shadow_hands", "shadow_hands")
    inst.AnimState:OverrideSymbol("snap_fx", "player_actions_fishing_ocean_new", "snap_fx")

    --Additional effects symbols for hit_darkness animation
    inst.AnimState:AddOverrideBuild("player_hit_darkness")
    inst.AnimState:AddOverrideBuild("player_receive_gift")
    inst.AnimState:AddOverrideBuild("player_actions_uniqueitem")
    inst.AnimState:AddOverrideBuild("player_wrap_bundle")
    inst.AnimState:AddOverrideBuild("player_lunge")
    inst.AnimState:AddOverrideBuild("player_attack_leap")
    inst.AnimState:AddOverrideBuild("player_superjump")
    inst.AnimState:AddOverrideBuild("player_multithrust")
    inst.AnimState:AddOverrideBuild("player_parryblock")
    inst.AnimState:AddOverrideBuild("player_emote_extra")
    inst.AnimState:AddOverrideBuild("player_boat_plank")
    inst.AnimState:AddOverrideBuild("player_boat_net")
    inst.AnimState:AddOverrideBuild("player_boat_sink")
    inst.AnimState:AddOverrideBuild("player_oar")

    inst.AnimState:AddOverrideBuild("player_actions_fishing_ocean_new")
    inst.AnimState:AddOverrideBuild("player_actions_farming")
    inst.AnimState:AddOverrideBuild("player_actions_cowbell") 


    inst.DynamicShadow:SetSize(1.3, .6)

    -- inst.Light:SetIntensity(.8)
    -- inst.Light:SetRadius(.5)
    -- inst.Light:SetFalloff(.65)
    -- inst.Light:SetColour(255 / 255, 255 / 255, 236 / 255)
    -- inst.Light:Enable(false)

    inst.Light:SetIntensity(.8)
    inst.Light:SetRadius(1.3)
    inst.Light:SetFalloff(.65)
    inst.Light:SetColour(255 / 255, 255 / 255, 236 / 255)
    inst.Light:Enable(false)

    inst.LightWatcher:SetLightThresh(.075)
    inst.LightWatcher:SetMinLightThresh(0.61) --for sanity.
    inst.LightWatcher:SetDarkThresh(.05)

    MakeCharacterPhysics(inst, 75, .5)

    inst:AddTag("npc")
    inst:AddTag("NPC")
    inst:AddTag("trader")
    inst:AddTag("alltrader") ---
    inst:AddTag("companion")
    -- inst:AddTag("npc_ghost")
    -- inst:AddTag("playerghost")
    -- inst:AddTag("player")

    ---------------------------------------------------------------------------------------------
    -- talker 组件   net_bool 帮助处理偏移
    local TALLER_TALKER_OFFSET = Vector3(0, -700, 0)
    local DEFAULT_TALKER_OFFSET = Vector3(0, -400, 0)
    inst:AddComponent("talker")
    inst.components.talker.offset = DEFAULT_TALKER_OFFSET
    -- local function GetTalkerOffset(inst)

    --     if inst:HasTag("npc_ghost") then
    --         return TALLER_TALKER_OFFSET
    --     else
    --         return DEFAULT_TALKER_OFFSET
    --     end
    -- end
    -- inst.components.talker:SetOffsetFn(GetTalkerOffset)
    inst.__is_ghost = net_bool(inst.GUID, "npc_events_netvars", "net_bool_is_ghost")
    
    inst:ListenForEvent("RPC_Remove",inst.Remove)

    if not TheWorld.ismastersim then
        inst:ListenForEvent("net_bool_is_ghost",function(inst)
            local value = inst.__is_ghost:value()
            if value == true then
                inst.components.talker.offset = TALLER_TALKER_OFFSET
            else
                inst.components.talker.offset = DEFAULT_TALKER_OFFSET
            end
        end)

    else
        inst:ListenForEvent("ghost",function()
            inst.components.talker.offset = TALLER_TALKER_OFFSET
            inst.__is_ghost:set(true)
        end)

        inst:ListenForEvent("respawn_from_ghost_base",function()
            inst.components.talker.offset = DEFAULT_TALKER_OFFSET
            inst.__is_ghost:set(false)
        end)

    end

    ---------------------------------------------------------------------------------------------
    inst.entity:SetPristine()


    inst.soundsname = "wilson"

    ---------------------------------------------------------------------------------------------
    ---  SGWilson.lua 里必须要用的
    --- 【来域外新】增API
    inst.IsInAnyStormOrCloud = function() return false  end
    inst.IsChannelCasting = function() return false  end
    ---------------------------------------------------------------------------------------------

    if not TheWorld.ismastersim then -----------------------------------------------------------------------------------------------------------------------
        return inst
    end

    -----------------------------------------------------------------------
    ---名字  和  检查
    inst:AddComponent("inspectable") --可检查组件
    inst:AddComponent("named")
    inst:DoTaskInTime(0,function()        
        local function firstToUpper(str)
            return (str:gsub("^%l",string.upper))
        end
        inst.components.named:SetName(firstToUpper(inst.prefab_base))
        local language = inst.npc_base_lib:Get_Language()
        if language == "ch" then
            STRINGS.CHARACTERS.GENERIC.DESCRIBE[string.upper(inst.prefab)] = "又一个冒险家"
        else
            STRINGS.CHARACTERS.GENERIC.DESCRIBE[string.upper(inst.prefab)] = "Another adventurer"
        end
        
        local tempTable =  Get_Name_And_Inspect_Table() or {}
        if tempTable[inst.prefab_base] then
            if tempTable[inst.prefab_base].name then
                inst.components.named:SetName(tempTable[inst.prefab_base].name)
            end
            if tempTable[inst.prefab_base].inspect then
                inst.components.inspectable:SetDescription(tempTable[inst.prefab_base].inspect)
            end
        end

    end)
    -----------------------------------------------------------------------

    inst:AddComponent("locomotor") -- locomotor must be constructed before the stategraph

    inst:AddComponent("hunger")
    inst.components.hunger:Pause()

    -----------------------------------------------------------------------
    -- 避免NPC被意外操作 弄死的 的措施
    local max_health = 5000
    inst:AddComponent("health")
    inst.components.health:SetMaxHealth(max_health)
    inst.components.health.DoDelta_old = inst.components.health.DoDelta
    inst.components.health.DoDelta = function(self,amount,...)
        self.currenthealth = max_health
        local ret = { self:DoDelta_old(-0.1,...) }
        self.currenthealth = max_health
        return unpack(ret)
    end
    -- inst:ListenForEvent("attacked",function()
    --     inst.components.health.currenthealth = max_health
    -- end)
    -----------------------------------------------------------------------
    inst:AddComponent("sanity")
    inst.components.sanity:SetMax(5000)
    inst.components.sanity.ignore = true

    inst:AddComponent("temperature")
    inst.components.temperature:SetTemp(TUNING.STARTING_TEMP)
    inst:AddComponent("combat")


    -------------------------------------------------------------------------
    -- 物品栏 和物品接收 相关
    -- 衣服类装备 普遍没有 com_tradable,无法被给到NPC。需要特殊手段添加给物品
    local function ShouldAcceptItem(inst, item)
        return item.components.inventoryitem ~= nil
    end
    local function OnGetItem(inst, giver, item)
        print("OnGetItem ,Giver :",giver,item)
    end
    local function abletoaccepttest(inst,item,giver)
        if item.components.inventoryitem ~= nil then
            return true
        end
    end
    inst:AddComponent("inventory") --- 物品栏
    inst.components.inventory.maxslots = 15

    inst:AddComponent("trader")
    inst.components.trader:SetAcceptTest(ShouldAcceptItem)
    inst.components.trader.onaccept = OnGetItem
    inst.components.trader.deleteitemonaccept = false
    inst.components.trader:SetAbleToAcceptTest(abletoaccepttest)

    -------------------------------------------------------------------------


    inst:AddComponent("debuffable")
    inst.components.debuffable:SetFollowSymbol("headbase", 0, -200, 0)

    inst:AddComponent("rider")

    inst:AddComponent("skinner")
    inst:AddComponent("bloomer")

    inst:AddComponent("leader")

    inst:AddComponent("npc_base_lib")
    inst:AddComponent("npc_everything_data")
    -------------------------------------------------------------------------------------------------------
    ---- SGwilson.lua 里必须用到的 func
        inst.GetStormLevel = function() return 0 end
        inst.IsCarefulWalking = function() return false  end
        inst.IsTeetering = function() return false  end
        inst.IsActing = function(self)
            return self.sg:HasStateTag("acting") or (self._is_onstage_task ~= nil)
        end
        inst.IsHUDVisible = function(...) return true end
        inst.OnWakeUp = function(...) end
        inst.OnSleepIn = function(...) end
        
        inst.ShowActions = function(...) end
        -- inst.IsInAnyStormOrCloud = function(...) return false end -- cilent 也有，不能只在这添加
    -------------------------------------------------------------------------------------------------------
    --- 鬼魂状态切换相关
        inst.ShowHUD = function(...)    end
        inst.SetCameraDistance = function(...)    end

        inst:ListenForEvent("respawn_from_ghost_base",function()
            inst.AnimState:SetBuild(inst.prefab_base)
            inst.AnimState:SetBank("wilson")
            inst:SetStateGraph("SGwilson")
            inst:RemoveTag("ghost_npc")
            MakeCharacterPhysics(inst, 75, .5)

        end)
        inst.ApplySkinOverrides = function()  
            inst:PushEvent("respawn_from_ghost_base")
        end
        ---------------------------------------------------------------------
        -- -- 使用这两个切换鬼魂状态
        inst:ListenForEvent("ghost",function(inst)
            local ghost_build_name = "ghost_"..inst.prefab_base.."_build"
            local bank_name = "ghost"
            inst.AnimState:SetBuild(ghost_build_name)
            inst.AnimState:SetBank(bank_name)
            inst:SetStateGraph("SGwilsonghost")
            inst:AddTag("ghost_npc")
            MakeGhostPhysics(inst, 1, .5)
            print("npc become ghost")
        end)

        inst:ListenForEvent("respawn_from_ghost",function()
            MakeCharacterPhysics(inst, 75, .5)

            -- local FX = SpawnPrefab("npc_ghost_fx_for_respawn")
            local FX = inst:SpawnChild("npc_ghost_fx_for_respawn")
            local ghost_build_name = "ghost_"..inst.prefab_base.."_build"
            local bank_name = "ghost"
            FX.AnimState:SetBuild(ghost_build_name)
            FX.AnimState:SetBank(bank_name)
            -- FX.Transform:SetPosition(inst.Transform:GetWorldPosition())
            FX.AnimState:PlayAnimation("shudder",false)
            FX.AnimState:PushAnimation("brace",false)
            FX.AnimState:PushAnimation("transform",false)
            FX:ListenForEvent("animqueueover",FX.Remove)


            inst.AnimState:SetBuild(inst.prefab_base)
            inst.AnimState:SetBank("wilson")
            inst:SetStateGraph("SGwilson")
            inst.sg:GoToState("reviver_rebirth")
            print("npc respawn form ghost")

        end)
        inst:ListenForEvent("respawnfromghost",function()
            inst:PushEvent("respawn_from_ghost")
        end)
    -------------------------------------------------------------------------------------------------------

    -------------------------------------------------------------------------------------------------------
    --- 玩家链接相关
        inst:ListenForEvent("player_link",function(inst,tar_player)
            inst.__link_player = tar_player
            inst.npc_base_lib:Set_Linked_Player(tar_player)
            if tar_player and tar_player:HasTag("player") then
                tar_player.__link_npc = inst
            end
        end)
        inst:ListenForEvent("player_link2",function(inst,tar_player)
            inst.__link_player = tar_player
            inst.npc_base_lib:Set_Linked_Player(tar_player)
        end)

        inst:ListenForEvent("player_unlink",function()
            if inst.__link_player and inst.__link_player.__link_npc == inst then
                inst.__link_player.__link_npc = nil
            end
        end)

        inst:DoTaskInTime(0.1,function()
            if inst.Ready == true then
                return
            end

            if inst.__link_player == nil then
                inst:Remove()
                print("warning : npc.__link_player == nil , inst:Remove() ")
            else
                print("npc create and  link succeed")
            end
        end)

        function inst:GetPlayer()
            return self.__link_player
        end
    -------------------------------------------------------------------------------------------------------
    
    inst:SetStateGraph("SGwilson")
    
    return inst
end
 

local function Fn()
    local Fx = Class()

    function Fx:Common_Npc_Create()
        local ret = fn()
        return ret
    end


    return Fx
end

return Fn